package com.djq.hhc.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.djq.hhc.dataprocess.MyStringUtils;
import com.djq.hhc.dataprocess.RegexUtils;
import com.djq.hhc.dto.UserInfoDto;
import com.djq.hhc.feign.CreditInfoFeignClient;
import com.djq.hhc.holder.UserHolder;
import com.djq.hhc.jwt.JwtUtils;
import com.djq.hhc.mapper.UserInfoMapper;
import com.djq.hhc.model.user.UserInfo;
import com.djq.hhc.redis.CacheClient;
import com.djq.hhc.result.Result;
import com.djq.hhc.result.ResultCodeEnum;
import com.djq.hhc.service.UserInfoService;
import com.djq.hhc.template.MailTemplate;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.djq.hhc.redis.RedisConstants.*;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: dadd7
 * @Date: 2023/02/25/下午 8:44
 * @Description:
 */

@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService{

    @Autowired
    private MailTemplate mailTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private CreditInfoFeignClient creditInfoFeignClient;

    @Override
    public Result sendMail(String mail) {
        //验证邮箱，工具包正则表达式
        if (RegexUtils.isEmailInvalid(mail)) {
            //不符合，返回错误信息
            return Result.fail().code(ResultCodeEnum.EMAIL_ERROR.getCode()).message(ResultCodeEnum.EMAIL_ERROR.getMessage());
        }

        //符合，生成验证码
        String code = RandomUtil.randomNumbers(6);
        //保存验证码到redis
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+mail, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);
        //发送验证码,这个可能会抛出异常，异常就异常了，异常就把错误的qq邮箱和验证码存入redis,反正过期会删除
        mailTemplate.sendMail(mail, code);
        //返回
        return Result.ok().message("验证码发送成功，你注意查收");
    }

    @Override
    public Result loginVerification(String email, String code) {
        //校验邮箱
        if (RegexUtils.isEmailInvalid(email)) {
            return Result.fail().message("邮箱格式错误");
        }
        //校验验证码
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + email);
        if (cacheCode == null || !cacheCode.equals(code)) {
            return Result.fail().message("验证码错误");
        }

        //删除验证码
        stringRedisTemplate.delete(LOGIN_CODE_KEY + email);

        //查找用户是否存在
        UserInfo user = query().eq("email", email).one();
        if (user==null) {
            createUserWithEmail(email);
        }
        //查找新保存的用户信息
        user = query().eq("email", email).one();
        //用户存在，保存用户信息到redis,生成token作为登录令牌
        UserInfoDto userInfoDto = BeanUtil.copyProperties(user, UserInfoDto.class);
        Map<String, Object> userMap = BeanUtil.beanToMap(userInfoDto, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)                   //Long转value要用String.valueOf()
                        .setFieldValueEditor((fieldName, fieldValue) -> String.valueOf(fieldValue)));//把属性值转换成String的，刚刚没写这个，报错java.lang.Long cannot be cast to java.lang.String（2023.03.04djq）
        String token = JwtUtils.getToken(userMap);
        stringRedisTemplate.opsForHash().putAll(LOGIN_TOKEN_KEY + token, userMap);
        stringRedisTemplate.expire(LOGIN_TOKEN_KEY + token, LOGIN_TOKEN_TTL, TimeUnit.MINUTES);
        //返回token
        return Result.ok(token).message("登录成功");
    }

    @Override
    public Result updateUserById(UserInfo userInfo) {
        //【头像重新上传（暂时先不管）】
        //昵称格式
        if (RegexUtils.isUserNameInvalid(userInfo.getNickName())) {
            return Result.fail().message("昵称格式：4~12位的中文、字母、数字");
        }
        //花花号格式
        if (RegexUtils.isHHIDInvalid(userInfo.getHhid())) {
            return Result.fail().message("花花号格式：4~20位的字母 、数字、下划线");
        }
        //邮箱格式
        if (RegexUtils.isEmailInvalid(userInfo.getEmail())) {
            return Result.fail().message("请输入正确的QQ邮箱");
        }
        //密码格式
        if (RegexUtils.isPasswordInvalid(userInfo.getPassword())) {
            return Result.fail().message("密码格式：4~20位的字母 、数字、下划线");
        }
        //昵称不能重复
        UserInfo one = query().eq("nick_name", userInfo.getNickName()).ne("id", userInfo.getId()).one();
        if (one != null) {
            return Result.fail().message("该昵称已存在");
        }
        //花花号不重复
        one = query().eq("hhid", userInfo.getHhid()).ne("id", userInfo.getId()).one();
        if (one != null) {
            return Result.fail().message("该花花号已存在");
        }
        updateById(userInfo);
        return Result.ok().message("用户编辑成功");
    }

    @Override
    public Result logoutByToken(String token) {
        //基于Token获取redis中的用户
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash()
                .entries(LOGIN_TOKEN_KEY + token);
        //如果redis里面还有token，就删除
        if (!userMap.isEmpty()) {
            stringRedisTemplate.delete(LOGIN_TOKEN_KEY + token);
        }
        //删除UserHolder里面的用户
        UserInfoDto user = UserHolder.getUser();
        if (user != null) {
            UserHolder.removeUser();
        }
        return Result.ok().message("退出登录成功");
    }

    //获取当前登录用户的信息，使用feign调取credit的信息
    @Override
    public Result getNowUserWithCredit(String token) {
        String key = NOW_USER_KEY + UserHolder.getUser().getId();
        String json = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(json)) {
            //存在，直接返回
            return Result.ok(JSONUtil.toBean(json, UserInfoDto.class));
        }
        //缓存重建，在数据库里查找
        UserInfoDto user = UserHolder.getUser();
        Result creditByUserId = creditInfoFeignClient.getCreditByUserId(token, String.valueOf(user.getId()));
        LinkedHashMap<String, Object> data = (LinkedHashMap<String, Object>) creditByUserId.getData();
        user.setCredit((Integer) data.get("credit"));
        user.setId(UserHolder.getUser().getId());
        user.setAvatar(UserHolder.getUser().getAvatar());
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(user), NOW_USER_TTL, TimeUnit.HOURS);
        return Result.ok(user);
    }

    //正在登录的用户修改自己的信息
    //这个需要更新数据库并且删除redis缓存，操作两个库，需要增加事务
    @Transactional
    @Override
    public Result updateNowUser(String token, UserInfo userInfo) {
        Long id = UserHolder.getUser().getId();
        //昵称不能重复
        UserInfo one = query().eq("nick_name", userInfo.getNickName()).ne("id", id).one();
        if (one != null) {
            Result.fail().message("该昵称已存在");
        }
        //昵称格式错误
        if (RegexUtils.isUserNameInvalid(userInfo.getNickName())) {
            return Result.fail().message("昵称格式：4~12位的中文、字母、数字");
        }
        //花花号不能重复
        one = query().eq("hhid", userInfo.getHhid()).ne("id", id).one();
        if (one != null) {
            return Result.fail().message("该花花号已存在");
        }
        //花花号格式
        if (RegexUtils.isHHIDInvalid(userInfo.getHhid())) {
            return Result.fail().message("花花号格式：4~20位的字母 、数字、下划线");
        }
        //简介不能超过50字
        if (userInfo.getBrief().length() > 50) {
            return Result.fail().message("简介不能超过50字");
        }
        //更新数据库
        userInfo.setId(id);
        updateById(userInfo);
        //删除缓存
        String key = NOW_USER_KEY + UserHolder.getUser().getId();
        stringRedisTemplate.delete(key);
        //修改UserHolder中的用户信息
        UserHolder.removeUser();//让再次生成吧！
        //改一下redis中token的user
        userInfo = getById(id);
        UserInfoDto userInfoDto = BeanUtil.copyProperties(userInfo, UserInfoDto.class);
        Map<String, Object> userMap = BeanUtil.beanToMap(userInfoDto, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)                   //Long转value要用String.valueOf()
                        .setFieldValueEditor((fieldName, fieldValue) -> String.valueOf(fieldValue)));//把属性值转换成String的，刚刚没写这个，报错java.lang.Long cannot be cast to java.lang.String（2023.03.04djq）
        stringRedisTemplate.opsForHash().putAll(LOGIN_TOKEN_KEY + token, userMap);
        stringRedisTemplate.expire(LOGIN_TOKEN_KEY + token, LOGIN_TOKEN_TTL, TimeUnit.MINUTES);
        return Result.ok().message("更改用户信息成功");
    }

    private void createUserWithEmail(String email) {
        UserInfo userInfo = new UserInfo();
        userInfo.setEmail(email);
        userInfo.setNickName("hhc"+ MyStringUtils.getQQNum(email));
        userInfo.setBrief("这个人还没有简介~");
        userInfo.setLimits(0); //默认是普通用户
        userInfo.setHhid("hhc"+ MyStringUtils.getQQNum(email));
        save(userInfo);
    }
}